/*
 * A moving object is a moving query. At the same time, the focal object can be 
 * result of other moving queries.  
 */
package field;

import geometry.Circle;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.PriorityQueue;

public class MovingObject extends Circle {
	int oid;
	int radius;
	Server currS;
	Cell currC; // current cell

	HashSet<Cell> fullCells;
	HashSet<Cell> partialCells;
	HashSet<Integer> currResult;// current set of mo in the query result
	HashSet<Integer> satisfiedQueries; // list of mcq's this moving
	// object satisfies
	Point2D nextP; // used when reading t_(i+1) data at time t_i
	Point2D prevP; // used to determine if a mo moves into a new cell
	Velocity speed;

	// CellID registeredCell;

	// HashMap<Integer, Boolean> monitoringQueries;
	//
	// ArrayList<Query> ownQueries;

	Field field;// used to compute enter cell events.

	// public MovingObject(int ID, Point loc, Speed spd, int timeStamp,
	// CellID cid, Field field) {
	// this.oid = ID;
	// this.p = loc;
	// this.v = spd;
	// this.ts = timeStamp;
	// this.registeredCell = cid;
	// this.ownQueries = new ArrayList<Query>();
	// this.monitoringQueries = new HashMap<Integer, Boolean>();
	// this.field = field;
	// }

	public MovingObject(int ID, int radius, Point2D center, Velocity spd,
			int timeStamp, Cell currCell, Field field) {
		super(center, radius);
		this.oid = ID;
		this.radius = radius;
		this.prevP = null;
		this.speed = spd;
		this.currC = currCell;
		// this.ownQueries = new ArrayList<Query>();
		// this.monitoringQueries = new HashMap<Integer, Boolean>();
		this.field = field;
		fullCells = new HashSet<Cell>();
		this.partialCells = new HashSet<Cell>();

		this.currResult = new HashSet<Integer>();
		this.satisfiedQueries = new HashSet<Integer>();
	}

	public void addFullCells(Cell c) {
		fullCells.add(c);
	}

	/**
	 * Add moving object id into the result
	 * 
	 * @param moid
	 */
	public void addResultObject(int moid) {
		this.currResult.add(moid);
	}

	public void addSatisfiedQuery(int movingQueryId) {
		this.satisfiedQueries.add(movingQueryId);
	}

	public void addPartCells(Cell c) {
		partialCells.add(c);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.geom.Ellipse2D#equals(java.lang.Object) To ensure the
	 * uniquness in the set.
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof MovingObject) {
			return this.oid == ((MovingObject) obj).oid;
		}
		return false;
	}

	public double getRadius() {
		return this.radius;
	}

	public Velocity getVelocity() {
		return speed;
	}

	/**
	 * add the mo id into current result
	 * 
	 * @param mo
	 */
	public void handleEnterMO(MovingObject mo) {

		this.currResult.add(mo.oid);
	}

	/**
	 * adjust the event queue for the moving object qmo
	 * 
	 * @param qmo
	 * @param speed
	 * @param oldv
	 * @param currentTS
	 */
	public void handleResultMoVelocityChange(int changeTS, MovingObject qmo,
			Velocity oldv, Velocity newV) {
		// TODO adjust the event queue based on the change

	}

	public Point2D locationAfterTime(double timeT) {
		double newX = this.getCenterX() + this.speed.xSpeed * timeT;
		double newY = this.getCenterY() + this.speed.ySpeed * timeT;
		Point2D newLoc = new Point2D.Double(newX, newY);
		return newLoc;
	}
	
	
	/**
	 * @param focolLoc
	 * @param cd
	 * @return -1 if the distance between this object and the focal location is
	 *         not smaller than the given distance. otherwise, the distance is
	 *         returned.
	 */
	public double nearer(Point2D focolLoc, double cd) {
		double disToFocol = this.getCenterP().distance(focolLoc);
		if (disToFocol < cd) {
			return disToFocol;
		} else {
			return -1;
		}
	}

	// public Msg_o2s_enterCell calCrossCellTime() {
	// assert (this.nextP != null) : "next location is not known";
	// Cell currentCell = this.field.CMatrix.getCell(this.registeredCell);
	// CellID cid = this.field.inWhichCell(this.nextP);
	// if (!this.registeredCell.equals(cid)) {
	// // test whether fly happens
	// assert (currentCell.neighboringCells.contains(cid)) :
	// "the object flies into a non-neighboring cell";
	// //todo: calculate event time
	// int fromObject = 0;
	// Msg_o2s_enterCell mec = new Msg_o2s_enterCell(this.ts + 1,
	// fromObject, this.oid, pre_cid, cid);
	// this.putMsgIntoMap((this.currentTS + 1), mec.toString());
	// this.handleEnterCell(mo.oid, pre_cid, cid);
	// }
	// }
	//
	// private double calCrossTime(){
	//
	// }

	public void printQueryResult() {
		System.out.println(this.toString());
		System.out.print("own query result:");
		System.out.println(currResult);
	}

	public void removeFullCell(Cell cell) {
		fullCells.remove(cell);
	}

	public void removePartCell(Cell cell) {
		partialCells.remove(cell);
	}

	public void removeMonitoringQuery(MovingObject movingQuery) {
		if (this.satisfiedQueries.contains(movingQuery)) {
			this.satisfiedQueries.remove(movingQuery);
		}
	}

	// public void setCenterP(Point2D point) {
	// super.setCenter(point);
	// }

	/**
	 * 
	 * @param newSpeed
	 */
	public void setVelocity(Velocity newSpeed) {
		this.speed = newSpeed;
	}

	public String toString() {

		String s = "MO oid:" + this.oid + ", location:"
				+ this.getCenterP().toString() + ", speed:"
				+ this.speed.toString() + ", radius: " + radius + ", cell:"
				+ currC;
		return s;
	}

	

}
